<template>
    <!-- #ifndef APP-NVUE -->
    <scroll-view 
        id="scrollView"
        class="lrl-virtual-list"
        :scroll-y="true"
        :lower-threshold="lowerThreshold"
        :upper-threshold="upperThreshold"
        :scroll-top="scrollTop"
        :scroll-with-animation="scrollWithAnimation"
        :enable-back-to-top="enableBackToTop"
        :refresher-enabled="refresherEnabled"
        :refresher-threshold="refresherThreshold"
        :refresher-default-style="refresherDefaultStyle"
        :refresher-background="refresherBackground"
        :refresher-triggered="refresherTriggered"
        :enable-flex="enableFlex"
        :scroll-anchoring="scrollAnchoring"
        @scroll="scrollViewEmit('scroll', $event);onScroll($event)"
        @click="scrollViewEmit('click', $event)"
        @scrolltoupper="scrollViewEmit('scrolltoupper', $event)"
        @scrolltolower="onScrolltolower($event);"
        @refresherpulling="onRefresherpulling($event), scrollViewEmit('refresherpulling', $event)"
        @refresherrefresh="onRefresherrefresh($event);scrollViewEmit('refresherrefresh', $event)"
        @refresherrestore="onRefresherrestore($event); scrollViewEmit('refresherrestore', $event)"
        @refresherabort="onRefresherabort($event);scrollViewEmit('refresherabort', $event)"
    >
        <view class="lrl-virtual-list__content">
            <!-- 虚拟列表原理：-->
            <!-- 1.用一个空view撑开list高度，使其可以滚动 -->
            <view class="lrl-virtual-list__replace" :style="[{ height: scrollHeight + 'px' }]"></view>
            <!-- 2.渲染内容通过滚动计算高度设置渲染内容以及垂直偏移量 -->
            <view 
                class="lrl-virtual-list__render" 
                :style="[
                    {
                        transform: `translateY(${ visibleTranslateY }px)`
                    }
                ]">
                <lrlVirtualListCell 
                    v-for="(item, index) in visibleData" 
                    :key="getCellKey(item, actualStartIndex + index)"
                    :id="actualStartIndex + index"
                    class="lrl-virtual-list__cell"
                    :itemData="item"
                    @updateSize="splice(actualStartIndex + index, 1, $event.height)"
                >
                    <slot :itemData="item" :index="actualStartIndex + index"></slot>
                </lrlVirtualListCell>
            </view>
        </view>
        
    </scroll-view>
    <!-- #endif -->
    <!-- #ifdef APP-NVUE -->
    <!-- app-nvue使用weex内置原生list组件，自带计算可视范围渲染，性能更好 -->
    <list 
        class="lrl-virtual-list"
    >
        <cell v-for="(item, index) in list" :key="getCellKey(index)">
            <slot :itemData="item" :index="index"></slot>
        </cell>
    </list>
    <!-- #endif -->
</template>

<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
<script src="./wxs/wxs.wxs" module="wxs" lang="wxs"></script>
<!-- #endif -->

<!-- app, h5使用renderjs -->
<!-- #ifdef APP-VUE || H5 -->
<script module="renderJs" lang="renderjs">
    import renderJs from './wxs/renderjs.js';
    export default {
        mixin: [renderJs]
    }
</script>
<!-- #endif -->

<script>
    import { timeWait, getRect, throttle } from '../../libs/util/tools.js';
    import scrollViewNotHandleProps from './scrollViewNotHandleProps.js';
    import PrefixSumManage from './PrefixSumManage.js';
    import initArrayMethod from './initArrayMethod.js';
    import mixin from '../../libs/mixin/mixin.js';
    import lrlVirtualListCell from './lrl-virtual-list-cell.vue';

    /**
     * VirtualList 虚拟列表
     * @description 虚拟列表，动态计算cell高度，只渲染可视范围数据，前端长列表顶级性能优化方案，轻松处理万级数据显示
     * @author liruilin
     * @tutorial 思路非原创，参照插件市场插件【xing-virtual-list】学习改良制作，https://ext.dcloud.net.cn/plugin?id=7129
     * @property {Array}         list           数据列表源
     * @property {String}        cellKeyMap     列表循环key字段映射，例如设置为id，将会取list数据里的id字段当做key，默认为index索引
     * @property {Number}        preRender      预渲染数量，加大可以缓解快速滑动导致的白屏，app-nvue 无效
     * 
     */
    export default {
        name: 'lrl-virtual-list',
        components: {
            lrlVirtualListCell
        },
        
        mixins: [
            mixin,
            // scrollView 默认props以及事件中间处理
            scrollViewNotHandleProps
        ],
        props: {
            list: {
                type: Array,
                required: true
            },
            // 预渲染数量，加大可以缓解快速滑动导致的白屏（前后）
            preRender: {
            	type: Number,
            	default: 10,
                validator: (value) => {
                    if(value < 1){
                        console.error('[lrl-virtual-list]：preRender 预渲染数量不能小于 1')
                    }
                	return value > 0;
                }
            },
            scrollIntoView: {
                type: String,
                default: ''
            },
            cellKeyMap: {
                type: String,
                default: ''
            },
        },
        data(){
            return {
                // 记录最后一次滚动位置
                lastScrollTop: 0,
                // 前缀和高度记录
                prefixSumHeight: [],
                itemHeight: [],
                // 渲染开始位置
                startIndex: 0,
                // 渲染结束位置
                endIndex: 0,
                // 滚动窗口高度
                scrollViewHeight: 0,
                // 
                refresheOffset: 0
            }
        },
        computed: {
            // 滚动内容总高度，用于占位
            scrollHeight(){
                // 前缀和最后一项即为高度
                return this.prefixSumHeight[this.prefixSumHeight.length - 1] || 0
            },
            // 可见数据
            visibleData(){
                return this.list.slice(this.actualStartIndex , this.actualEndIndex + 1 + 1 )
            },
            /**
             * 实际渲染： 预渲染 + 起点 + 终点 + 预渲染
             */
            // 实际渲染起点
            actualStartIndex(){
                return Math.max(this.startIndex - this.preRender + 1, 0)
            },
            // 实际渲染终点
            actualEndIndex(){
                return Math.min(this.endIndex + this.preRender, this.list.length - 1)
            },
            // 可见数据index索引 [10, .... ,20]
            visibleIndexs(){
                return this.visibleData.map((item, index) => this.actualStartIndex + index);
            },
            // 可视渲染区域垂直偏移量
            visibleTranslateY(){
                return this.prefixSumHeight[this.actualStartIndex - 1]
            },
            // 获取列表循环key
            getCellKey(){
                return (item, index) => {
                    return this.cellKeyMap ? item[this.cellKeyMap] : index
                }
            },
        },
        watch: {
            // scrollIntoView 需要内部自己实现
            scrollIntoView(id){
                
            },
            list: {
                handler(nList){
                    console.log('list变化了')
                    if(!nList.length){
                        this.splice(0, this.prefixSumHeight.length)
                    }
                    // this.splice(Math.max(nList.length - 1, 0), this.prefixSumHeight.length)
                }
            }
        },
        created() {
            this.PrefixSumManage = new PrefixSumManage({
                target: this, 
                property: 'prefixSumHeight'
            })
            this.PrefixSumManage.$on('updateChange', this.prefixSumUpdateChange, true)
        },
        mounted() {
            this.init()
            console.log(this.list)
        },
        methods: {
            async init(){
                // this.initListOrign()
                await this.queryScrollViewHeight()
                this.contentFullHandler()
            },
            /**
             * 判断内容是否沾满，未沾满添加数据
             * 主要用于初始化时自动填满列表数据
             */
            async contentFullHandler(){
                console.log('测量沾满')
                if(this.scrollHeight < this.scrollViewHeight && this.list[this.endIndex + 1]){
                    this.endIndex ++
                }
            },
            /**
             * 初始化包装list Array原型方法，监听改变
             */
            initListOrign(){
                let methods = {
                	push(add) {
                        console.log(' %c push', 'color: red;font-size: 22px;')
                	},
                	pop() {
                		
                	},
                	shift() {
                		
                	},
                	unshift() {
                		
                	},
                	sort() {
                		
                	},
                	reverse() {
                		
                	},
                	splice(agr) {

                	},
                    concat() {
                        
                    }
                }
                initArrayMethod(this.list, methods, this)
            },
            /**
             * 监听列表滚动
             */
            onScroll: throttle(function(e){
                const { scrollTop } = e.detail
                this.lastScrollTop = scrollTop
                this.positionContentIndex(scrollTop)
                // console.log(this.actualEndIndex)
            }),
            onScrolltolower($event){
                this.scrollViewEmit('scrolltolower', $event)
            },
            onRefresherpulling(e){
                this.refresheOffset = e.detail.deltaY
            },
            onRefresherrefresh(e){
                this.refresheOffset = 45
            },
            onRefresherrestore(e){
                this.refresheOffset = 0
            },
            onRefresherabort(e){
                this.refresheOffset = 0
            },
            consoleInfo(){
                console.log('start end', this.startIndex, this.endIndex) 
                console.log('start end actual', this.actualStartIndex, this.actualEndIndex) 
                console.log('lastScrollTop', this.lastScrollTop ) 
                console.log('+scrollViewHeight', this.lastScrollTop + this.scrollViewHeight) 
                console.log('prefixSumHeight',  this.prefixSumHeight)
                console.log('itemHeight',  this.itemHeight)
                console.log('list',  this.list.length)
            },
            /**
             * 定位可视数据位置
             * @param {Object} scrollTop
             */
            positionContentIndex(scrollTop){
                this.startIndex = this.PrefixSumManage.binarySearch(this.refresheOffset + scrollTop, this.startIndex)
                this.endIndex = this.PrefixSumManage.binarySearch(this.refresheOffset + scrollTop + this.scrollViewHeight, this.startIndex)
                // console.log('重获位置')
                // console.log('start end', this.startIndex, this.endIndex) 
                // console.log(scrollTop, scrollTop + this.scrollViewHeight, this.actualStartIndex, this.actualEndIndex) 
                // console.log(this.prefixSumHeight, this.visibleData)
            },
            /**
             * 前缀和更新后触发
             */
            prefixSumUpdateChange(){
                // console.log('完成', this.scrollHeight, this.scrollViewHeight)
                this.positionContentIndex(this.lastScrollTop)
                this.contentFullHandler()
            },
            /**
             * @param {Object} index
             * @param {Object} leng
             */
            async splice(index, leng, ...measureHeights){
                console.log(index, measureHeights)
                this.PrefixSumManage.splice(index, leng, ...measureHeights)
                this.itemHeight.splice(index, leng, ...measureHeights)
            },
            // 获取预渲染各cell高度
            async getMeasureHeight(){
                const res = await getRect('.measure', this, true)
                return res.map(e => e.height)
            },
            // 查询滚动窗口高度
            async queryScrollViewHeight(){
               this.scrollViewHeight = (await getRect('#scrollView', this)).height
            }
        }
    }
</script>

<style lang="scss" scoped>
    .lrl-virtual-list{
        
        &__replace{
            // background-color: blueviolet;
        }
        
        &__content{
            position: relative;
        }
        
        &__render{
            position: absolute;
            overflow: hidden;
            top: 0;
            left: 0;
            right: 0;
            // background-color: rgba(150,150,150, 0.3);
        }
        
        &__cell{
            overflow: hidden;
        }
    }
</style>